﻿using System;
using System.Configuration;
using System.Data.Objects;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Timers;
using Citi.Helpers;
using Citi.Scheduler.Domain;
using Citi.Scheduler.Domain.Utils;

namespace Citi.Scheduler.WindowsServices
{
    public partial class Scheduler : ServiceBase
    {
        private const string EventSource = "Citi Scheduler Service";
        private const string EventLogName = "Citi Scheduler Service";
        private static string NetworkUser { get { return ConfigurationManager.AppSettings["NetworkUser"]; } }
        private static string NetWorkPassword { get { return ConfigurationManager.AppSettings["NetWorkPassword"]; } }
        private static string Domain { get { return NetworkUser.Split('\\')[0]; } }
        private static string UserWithoutDomain { get { return NetworkUser.Split('\\')[1]; } }
        private readonly Domain.RoutineSchedule routineSchedule = new Domain.RoutineSchedule();
        private static bool ProcessRunning { get; set; }
        private static Machine Machine { get; set; }

        private Timer timer = new Timer();
        //Thread thread;
        //AutoResetEvent stopEvent = new AutoResetEvent(false);

        private string ErrorLog
        {
            get
            {
                var tempFolder = Path.GetDirectoryName(ConfigurationManager.AppSettings["TempFolder"]);
                return Path.Combine(tempFolder, "ErrorLog.txt");
            }
        }

        private string ExecutionLog
        {
            get
            {
                var tempFolder = Path.GetDirectoryName(ConfigurationManager.AppSettings["TempFolder"]);
                return Path.Combine(tempFolder, "ScheduleExecutionLog.txt");
            }
        }

        public Scheduler()
        {
            InitializeComponent();

            try
            {
                if (!EventLog.SourceExists(EventSource)) EventLog.CreateEventSource(EventSource, EventLogName);
            }
            catch { }
        }

        protected override void OnStart(string[] args)
        {
            var writer = new ConsoleInFile(ExecutionLog);
            Console.SetOut(writer);

            Machine = new Machine().GetByHostName(Environment.MachineName, MachineCenter.Scheduler);

            if (Machine == null)
            {
                EventLog.WriteEntry("Machine isn't a Scheduler Center Machine", EventLogEntryType.Information);
                throw new Exception("Machine does not exist in the database");
                //return;
            }

            ProcessRunning = false;

            timer.Elapsed += TimerOnElapsed;
            timer.Interval = 120000;
            timer.Start();

            //thread = new Thread(ExecuteSchedule);
            //thread.Start();
        }

        public void StartDebug(string machineName)
        {
            Machine = new Machine().GetByHostName(machineName, MachineCenter.Scheduler);
            ExecuteSchedule();
        }

        private void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            if (ProcessRunning) return;

            if (Machine.BackupPosition.HasValue && Machine.BackupPosition > 0)
            {
                var machinesBackup = Machine.SelectByCenterBackupOrder(MachineCenter.Scheduler).Where(x => x.BackupPosition < Machine.BackupPosition);

                foreach (var machine in machinesBackup)
                {
                    var service = new MachineService(machine, ServiceName, Domain, NetworkUser, NetWorkPassword);

                    if (service.GetServiceStatus() == ServiceControllerStatus.Running)
                    {
                        Console.WriteLine("{0} Backup is not needed at this time", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));
                        return;
                    }
                }
            }

            ExecuteSchedule();
        }

        //static DateTime LastSchedulerExecution;

        private void ExecuteSchedule()
        {
            //while (true)
            //{
            try
            {
                ProcessRunning = true;

                timer.Stop();

                SetOverwriteChanges(Context.Instance.RoutineExecutions);
                SetOverwriteChanges(Context.Instance.Routines);
                SetOverwriteChanges(Context.Instance.RoutineSchedules);
                SetOverwriteChanges(Context.Instance.Schedules);
                SetOverwriteChanges(Context.Instance.RoutineExecutions);
                SetOverwriteChanges(Context.Instance.RoutineOnDemands);
                SetOverwriteChanges(Context.Instance.Schedules);

                Console.WriteLine();
                Console.WriteLine("{0} Checking routines", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));

                routineSchedule.SendRoutinesToExecutionQueue(true);
                EndOpenedExecution();

                Console.WriteLine("{0} Finished", DateTime.Now.ToString("dd/MM/yyy HH:mm:ss"));
                Console.WriteLine();
            }
            catch (Exception e)
            {
                LogUtil.PathLogErro = ErrorLog;
                LogUtil.LogError(EventLog, e);
            }
            finally
            {
                //Thread.Sleep(120000);
                timer.Start();
                ProcessRunning = false;
            }
            //}
        }

        private void EndOpenedExecution()
        {
            var routinesOpened = new RoutineExecution().SelectAllActive<RoutineExecution>()
                .Where(x => !x.EndsOn.HasValue && x.StartsOn.HasValue && x.StartsOn.Value.Date == DateTime.Today
                    && (DateTime.Now - x.StartsOn.Value).TotalMinutes > x.Routine.EstimatedTime * (x.Routine.PercentageAcceptableVariation / 100 + 1) + 15);

            foreach (var routineExecution in routinesOpened)
            {
                var lastHistory = routineExecution.LastHistory;
                //routineExecution.EndsOn = DateTime.Now;
                //routineExecution.Save(routineExecution);
                routineExecution.SetEndsOn();

                if (routineExecution.RoutineExecutionHistories.Any(x => x.Status == (short)RoutineExecutionStatus.Executed))
                {
                    routineExecution.SetEndsOn();
                    //routineExecution.EndsOn = DateTime.Now;
                    //routineExecution.Save(routineExecution);
                }

                if (routineExecution.RoutineExecutionHistories.Any(x => x.Status == (short)RoutineExecutionStatus.Error))
                    ExecutionAction.InsertNewTentative(routineExecution);

                if (routineExecution.RoutineExecutionHistories.Any(x => x.Status == (short)RoutineExecutionStatus.Executed || x.Status == (short)RoutineExecutionStatus.Error
                    || x.Status == (short)RoutineExecutionStatus.ExecutedWithError || x.Status == (short)RoutineExecutionStatus.Canceled
                    || x.Status == (short)RoutineExecutionStatus.CanceledAfterWaiting || x.Status == (short)RoutineExecutionStatus.Stopped))
                {
                    continue;
                }

                ExecutionAction.CurrentStatus = RoutineExecutionStatus.Stopped;
                RoutineExecutionHistory.InsertHistory(routineExecution.Id, ExecutionAction.CurrentStatus, HistoryDetail.UnexpectedError);

                try
                {
                    ExecutionAction.InsertNewTentative(routineExecution);
                }
                catch { }
            }
        }

        private static void SetOverwriteChanges<T>(ObjectSet<T> objectSet) where T : GeneralAction
        {
            try
            {
                objectSet.MergeOption = MergeOption.OverwriteChanges;
            }
            catch { }
        }

        protected override void OnStop()
        {
            timer.Stop();
            //stopEvent.Set();
            //thread.Join();
        }
    }
}